package com.googlecode.c4e.api;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext;

import com.googlecode.c4e.api.model.BuildResult;
import com.googlecode.c4e.api.model.Project;
import com.googlecode.c4e.api.model.ProjectGroup;
import com.googlecode.c4e.api.model.Server;
import com.googlecode.c4e.services.ContinuumConnector;
import com.googlecode.c4e.services.internal.NoopContinuumConnector;

/**
 * Provides methods for server and project management.
 * 
 * @author Tomasz Pik
 */
public class Registry extends Plugin {

	public static final String PLUGIN_ID = "com.googlecode.c4e.api";
	private static final String CONTINUUM_CONNECTOR_EXTENSION_POINT_ID = "com.googlecode.c4e.api.continuumConnector";

	private static Registry plugin;

	public Registry() {
		plugin = this;
	}

	public void start(BundleContext context) throws Exception {
		super.start(context);
	}

	public void stop(BundleContext context) throws Exception {
		plugin = null;
		super.stop(context);
	}

	public static Registry getDefault() {
		return plugin;
	}

	public boolean isContinuumConnectorAvailable() {
		if (this.getContinuunConnector() instanceof NoopContinuumConnector) {
			return false;
		}
		return true;
	}

	private ContinuumConnector getContinuunConnector() {
		try {
			IExtensionPoint point = Platform.getExtensionRegistry()
					.getExtensionPoint(CONTINUUM_CONNECTOR_EXTENSION_POINT_ID);
			if (point != null) {
				for (IConfigurationElement element : point
						.getConfigurationElements()) {
					return (ContinuumConnector) element
							.createExecutableExtension("continnumConnector");
				}
			}
			this.getLog().log(
					new Status(Status.WARNING, PLUGIN_ID,
							"There is no extesion: "
									+ CONTINUUM_CONNECTOR_EXTENSION_POINT_ID
									+ " registered"));
			return new NoopContinuumConnector();
		} catch (Exception e) {
			this
					.getLog()
					.log(
							new Status(
									Status.ERROR,
									PLUGIN_ID,
									"Error getting extesion: "
											+ CONTINUUM_CONNECTOR_EXTENSION_POINT_ID,
									e));
			return new NoopContinuumConnector();
		}
	}

	public void discardServer(Server server) {
		if (server.getId() == null) {
			return;
		}
		String serverIdsString = getPluginPreferences().getString("servers");
		if ((serverIdsString == null) || (serverIdsString.trim().length() == 0)) {
			return;
		}
		String newServerIdsString = "";
		String[] serverIds = serverIdsString.split(",");
		for (String serverId : serverIds) {
			if (!serverId.trim().equals(server.getId().trim())) {
				if (newServerIdsString.length() > 0) {
					newServerIdsString = newServerIdsString + ",";
				}
				newServerIdsString = newServerIdsString + serverId;
			}
		}
		getPluginPreferences().setValue("servers", newServerIdsString);

		// TODO find a way for removing properties!
		getPluginPreferences()
				.setValue("server." + server.getId() + ".url", "");
		getPluginPreferences().setValue("server." + server.getId() + ".label",
				"");
		getPluginPreferences().setValue(
				"server." + server.getId() + ".username", "");
		try {
			Platform.flushAuthorizationInfo(new URL(server.getUrl()), server
					.getUsername(), "continuum");
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}

		savePluginPreferences();
	}

	public void saveServer(Server server) {
		if (server.getId() == null) {
			server.setId(String.valueOf(System.currentTimeMillis()));
		}
		String serverIdsString = getPluginPreferences().getString("servers");
		if ((serverIdsString == null) || (serverIdsString.trim().length() == 0)) {
			serverIdsString = server.getId();
		} else {
			String[] serverIds = serverIdsString.split(",");
			boolean found = false;
			for (String serverId : serverIds) {
				if (serverId.trim().equals(server.getId().trim())) {
					found = true;
				}
			}
			if (!found) {
				serverIdsString = serverIdsString + "," + server.getId();
			}
		}
		getPluginPreferences().setValue("servers", serverIdsString);
		getPluginPreferences().setValue("server." + server.getId() + ".url",
				server.getUrl());
		getPluginPreferences().setValue("server." + server.getId() + ".label",
				server.getLabel());
		getPluginPreferences().setValue(
				"server." + server.getId() + ".username", server.getUsername());

		Map authInfo = new HashMap();
		if (server.getPassword() != null) {
			authInfo.put("password", server.getPassword());
		}
		try {
			Platform.addAuthorizationInfo(new URL(server.getUrl()), server
					.getUsername(), "continuum", authInfo);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		savePluginPreferences();
	}

	public List<Server> listServers() {
		List<Server> result = new ArrayList<Server>();
		String servers = getPluginPreferences().getString("servers");
		String[] serverIds = servers.split(",");
		for (String serverId : serverIds) {
			if (serverId.trim().length() > 0) {
				Server server = new Server();
				server.setId(serverId);
				server.setUrl(getPluginPreferences().getString(
						"server." + serverId + ".url"));
				server.setLabel(getPluginPreferences().getString(
						"server." + serverId + ".label"));
				server.setUsername(getPluginPreferences().getString(
						"server." + serverId + ".username"));
				try {
					Map authInfo = Platform.getAuthorizationInfo(new URL(server
							.getUrl()), server.getUsername(), "continuum");
					if (authInfo != null) {
						server.setPassword((String) authInfo.get("password"));
					}
				} catch (MalformedURLException mue) {
					throw new RuntimeException(mue.getMessage(), mue);
				}
				result.add(server);
			}
		}

		return result;
	}

	public BuildResult lastBuildResult(Project project) throws Exception {
		return this.connect(project.getGroup().getServer())
				.getLatestBuildResult(project);
	}

	public List<ProjectGroup> listProjectGroups(Server server) throws Exception {
		return this.connect(server).getAllProjectGroups(server);
	}

	public Integer checkProjectState(Project project) throws Exception {
		ContinuumConnector continuumConnector = this.connect(project.getGroup()
				.getServer());
		return continuumConnector.checkProjectState(project.getId());
	}

	public List<Project> listProjects(ProjectGroup projectGroup)
			throws Exception {
		ContinuumConnector continuumConnector = this.connect(projectGroup
				.getServer());
		return continuumConnector.getProjects(projectGroup);
	}

	public List<BuildResult> listBuildResults(Project project) throws Exception {
		return connect(project.getGroup().getServer()).getBuildResults(project);
	}

	public void buildProject(Project project) throws Exception {
		this.connect(project.getGroup().getServer()).buildProject(
				project.getId());
	}

	public void verifyServer(Server server) throws Exception {
		this.connect(server).ping();
	}

	private ContinuumConnector connect(Server server) throws Exception {
		String username = server.getUsername();
		if (isEmpty(username)) {
			username = null;
		}
		String password = server.getPassword();
		if (isEmpty(password)) {
			password = null;
		}
		ContinuumConnector continuumConnector = this.getContinuunConnector();
		continuumConnector.connect(server);
		return continuumConnector;
	}

	private boolean isEmpty(String str) {
		return str == null || str.trim().length() == 0;
	}
}
